Skip to content

ffi: add shared-buffer fast path for numeric and pointer signatures#62918

Open
bengl wants to merge 4 commits intonodejs:mainfrom
bengl:bengl/shared-buffer-ffi
Open

ffi: add shared-buffer fast path for numeric and pointer signatures#62918
bengl wants to merge 4 commits intonodejs:mainfrom
bengl:bengl/shared-buffer-ffi

Conversation

@bengl
Copy link
Copy Markdown
Member

@bengl bengl commented Apr 23, 2026

Adds an ArrayBuffer-based invocation path for FFI functions whose
signatures are composed entirely of numeric types (i8..i64, u8..u64,
f32, f64, bool, char) and/or pointer types. The JS wrapper packs
arguments directly into a per-function AB via primordial DataView
setters and the C++ invoker (InvokeFunctionSB) reads them without
going through V8's FunctionCallbackInfo. Results are returned the same
way.

Pointer arguments use runtime dispatch: BigInt, null, and undefined take
the fast path, while Buffer, ArrayBuffer, ArrayBufferView, and String
fall back transparently to the classic InvokeFunction path via a
stashed _invokeSlow function. Signatures containing
non-numeric/non-pointer types also bypass the fast path.

The fast path is disabled on big-endian platforms.

Callers do not opt in, and the fast path is transparent in every way
users should rely on. One observable change: function wrappers returned
by library.getFunction, library.getFunctions, and library.functions
now have .length equal to the declared parameter count rather than
0. Code that relied on the previous value will need to be updated.


This is largely based on #46905, which is largely based on https://github.com/bengl/sbffi.


Benchmark output:

                                       confidence improvement accuracy (*)   (**)  (***)
ffi/add-f64.js n=10000000                     ***     34.11 %       ±1.28% ±1.70% ±2.23%
ffi/add-i32.js n=10000000                     ***     32.46 %       ±0.75% ±1.01% ±1.32%
ffi/getpid.js n=10000000                       **      1.89 %       ±1.24% ±1.67% ±2.19%
ffi/many-args.js n=10000000                   ***     -7.10 %       ±0.52% ±0.69% ±0.89%
ffi/pointer-bigint.js n=10000000              ***     60.00 %       ±0.85% ±1.14% ±1.49%
ffi/sum-buffer.js n=1000000 size=1024         ***     20.46 %       ±1.16% ±1.55% ±2.04%
ffi/sum-buffer.js n=1000000 size=16384        ***      2.72 %       ±0.74% ±0.98% ±1.28%
ffi/sum-buffer.js n=1000000 size=64           ***     32.15 %       ±2.82% ±3.77% ±4.95%

@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/performance

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Apr 23, 2026
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch from 5dea1bf to 7b165f9 Compare April 24, 2026 02:48
@bengl bengl marked this pull request as ready for review April 24, 2026 02:49
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch from 7b165f9 to 676f0ae Compare April 24, 2026 03:04
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

❌ Patch coverage is 95.78821% with 35 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.66%. Comparing base (fcff458) to head (844973a).
⚠️ Report is 4 commits behind head on main.

Files with missing lines Patch % Lines
src/node_ffi.cc 82.67% 11 Missing and 11 partials ⚠️
src/ffi/types.cc 89.55% 1 Missing and 6 partials ⚠️
lib/internal/ffi-shared-buffer.js 99.05% 3 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #62918      +/-   ##
==========================================
- Coverage   91.48%   89.66%   -1.83%     
==========================================
  Files         356      707     +351     
  Lines      150602   220033   +69431     
  Branches    23709    42196   +18487     
==========================================
+ Hits       137777   197286   +59509     
- Misses      12554    14613    +2059     
- Partials      271     8134    +7863     
Files with missing lines Coverage Δ
lib/ffi.js 96.59% <100.00%> (+0.02%) ⬆️
src/node_builtins.cc 76.09% <ø> (ø)
src/node_ffi.h 72.72% <ø> (ø)
lib/internal/ffi-shared-buffer.js 99.05% <99.05%> (ø)
src/ffi/types.cc 48.68% <89.55%> (ø)
src/node_ffi.cc 69.76% <82.67%> (ø)

... and 467 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@ShogunPanda ShogunPanda added the ffi Issues and PRs related to experimental Foreign Function Interface support. label Apr 24, 2026
Comment thread src/node_ffi.h Outdated
Comment thread src/node_ffi.cc Outdated
Comment thread src/node_ffi.cc Outdated
Comment thread src/node_ffi.cc Outdated
Comment thread src/ffi/types.cc Outdated
Comment thread src/ffi/types.cc Outdated
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch 2 times, most recently from 91c8e5f to b665e1a Compare April 24, 2026 15:44
bengl added 4 commits April 24, 2026 12:12
Adds microbenchmarks covering the common FFI call shapes so future
changes to the invoker can be evaluated:

- add-i32.js: 2-arg integer
- add-f64.js: 2-arg float
- many-args.js: 6-arg integer
- pointer-bigint.js: 1-arg pointer (BigInt)
- sum-buffer.js: pointer + length (Buffer)

A `common.js` helper resolves the fixture-library path from
`test/ffi/fixture_library` without pulling in the test harness, and
throws a clear message if the fixture hasn't been built yet.

Also adds `sum_6_i32` to the fixture library for the many-args case.
Adds an ArrayBuffer-based invocation path for FFI functions whose
signatures are composed entirely of numeric types (i8..i64, u8..u64,
f32, f64, bool, char) and/or pointer types. The JS wrapper packs
arguments directly into a per-function AB via primordial DataView
setters and the C++ invoker (`InvokeFunctionSB`) reads them without
going through V8's `FunctionCallbackInfo`. Results are returned the same
way.

Pointer arguments use runtime dispatch: BigInt, null, and undefined take
the fast path, while Buffer, ArrayBuffer, ArrayBufferView, and String
fall back transparently to the classic `InvokeFunction` path via a
stashed `_invokeSlow` function. Signatures containing
non-numeric/non-pointer types also bypass the fast path.

The fast path is disabled on big-endian platforms.

Callers do not opt in, and the fast path is transparent in every way
users should rely on. One observable change: function wrappers returned
by `library.getFunction`, `library.getFunctions`, and `library.functions`
now have `.length` equal to the declared parameter count rather than
`0`. Code that relied on the previous value will need to be updated.
…tures

Raise JS coverage on `lib/internal/ffi-shared-buffer.js` from 83.14% to
99.84% so the nodejs/node `coverage-linux-without-intl` workflow's 95%
threshold is met (the workflow was failing at 94.98%).

Two kinds of changes:

* Mark genuinely-unreachable defensive blocks with `/* c8 ignore */`.
  These are `ERR_INTERNAL_ASSERTION` throws that trigger only when the
  native and JS sides disagree on the shared-buffer ABI, plus the
  `nargs === 0` branch in `buildNumericWrapper` which is dead today
  because `IsSBEligibleSignature` rejects 0-arg signatures on the
  native side.

* Add fixtures and tests to exercise every arity of the void-return
  specialization ladder (1, 3, 4, 5, plus the 7+ rest-params fallback;
  2 and 6 were already covered), and to hit the `throwFFIArgCountError`
  branch in each value-return specialization (1..6) and in the
  pointer-dispatch wrapper.

No behavior change in the FFI runtime path itself.
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch from b665e1a to 844973a Compare April 24, 2026 16:42
@bengl bengl added the request-ci Add this label to start a Jenkins CI on a PR. label Apr 24, 2026
@bengl
Copy link
Copy Markdown
Member Author

bengl commented Apr 24, 2026

@addaleax I rebased, so now your review is stale.

@github-actions github-actions Bot removed the request-ci Add this label to start a Jenkins CI on a PR. label Apr 24, 2026
@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

@addaleax addaleax added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 24, 2026
@addaleax
Copy link
Copy Markdown
Member

The Windows failures here are definitely real

@addaleax addaleax removed the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Apr 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. ffi Issues and PRs related to experimental Foreign Function Interface support. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants